package com.jzo2o.market.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.domain.CouponWriteOff;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.mysql.utils.PageUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
    private static final int MILLION = 1000000;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ICouponService couponService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;


    /**
     * 新增优惠券活动&&编辑优惠券活动
     */
    @Override
    public void saveActivity(ActivitySaveReqDTO dto) {
        Activity activity = BeanUtil.toBean(dto, Activity.class);
        activity.setStatus(ActivityStatusEnum.NO_DISTRIBUTE.getStatus());//优惠券状态

        //校验时间等合法相关信息
        dto.check();

        //判断是新增还是修改
        if (ObjectUtils.isNull(dto.getId())) {
            //新增
            //若发布数量>0
            if (dto.getTotalNum() > 0) {
                activity.setStockNum(dto.getTotalNum());//优惠券库存
            }
            boolean result = save(activity);
            if (!result) {
                throw new DBException("新增优惠券活动失败");
            }
        } else {
            //编辑
            boolean update = lambdaUpdate().eq(Activity::getId, dto.getId())
                    .update(activity);
            if (!update) {
                throw new DBException("编辑优惠券活动失败");
            }
        }
    }


    /**
     * 分页查询优惠券活动接口
     *
     * @param dto
     * @return
     */
    @Override
    public PageResult<ActivityInfoResDTO> activityPage(ActivityQueryForPageReqDTO dto) {
        if (dto.getPageNo() == 0 || dto.getPageSize() == 0) {
            dto.setPageNo(1L);
            dto.setPageSize(10L);
        }
        Page<Activity> activityPage = PageUtils.parsePageQuery(dto, Activity.class);
        Page<Activity> page = lambdaQuery().eq(StringUtils.isNotBlank(dto.getName()), Activity::getName, dto.getName())
                .eq(ObjectUtils.isNotNull(dto.getStatus()), Activity::getStatus, dto.getStatus())
                .eq(ObjectUtils.isNotNull(dto.getType()), Activity::getType, dto.getType())
                .eq(ObjectUtils.isNotNull(dto.getId()), Activity::getId, dto.getId())
                .page(activityPage);
        List<ActivityInfoResDTO> activityInfoResDTOList = BeanUtil.copyToList(page.getRecords(), ActivityInfoResDTO.class);
        return new PageResult<>(page.getPages(), page.getTotal(), activityInfoResDTOList);
    }


    /**
     * 查询优惠券活动详情接口
     */
    @Override
    public ActivityInfoResDTO selectByActivityId(Long id) {
        Activity activity = getById(id);
        if (activity == null) {
            throw new ForbiddenOperationException("优惠券活动不存在");
        }
        ActivityInfoResDTO activityInfoResDTO = BeanUtil.toBean(activity, ActivityInfoResDTO.class);
        activityInfoResDTO.setReceiveNum(activity.getTotalNum() - activity.getStockNum());//领取数量
        //通过活动id查询核销数量
        int count = couponWriteOffService.count(Wrappers.<CouponWriteOff>lambdaQuery().eq(CouponWriteOff::getActivityId, id));
        activityInfoResDTO.setWriteOffNum(count);
        return activityInfoResDTO;
    }

    /**
     * 撤销活动
     */
    @Override
    public void revokeActivityById(Long id) {
        //判断活动id是否有效
        Activity activity = getById(id);
        if (activity == null) {
            throw new BadRequestException("活动不存在");
        }

        //通过活动id修改活动状态为作废状态
        boolean update = lambdaUpdate().eq(Activity::getId, id)
                .set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
                .update();
        if (!update) {
            throw new DBException("通过活动id修改活动状态为作废状态失败");
        }
        //修改本优惠卷未使用的记录用户为已过期
        LambdaUpdateWrapper<Coupon> set = Wrappers.<Coupon>lambdaUpdate()
                .eq(Coupon::getActivityId, id)
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .set(Coupon::getStatus, CouponStatusEnum.VOIDED.getStatus());
        boolean result = couponService.update(null, set);
        if (!result) {
            throw new DBException("修改本优惠卷未使用的记录用户为已过期失败");
        }
    }


    /**
     * 用户端抢券列表分页接口
     * tabtype  1-疯抢中  2-即将开始
     * NO_DISTRIBUTE(1, "待生效"), DISTRIBUTING(2, "进行中"), LOSE_EFFICACY(3, "已失效"),VOIDED(4, "作废");
     */
    @Override
    public List<ActivityInfoResDTO> queryForPage(Integer tabType) {
        //1.从redis中获取List<ActivityInfoResDTO>json数据
        Object json = redisTemplate.opsForValue().get(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST);
        //2.将json数据转为list对象
        List<ActivityInfoResDTO> activityInfoResDTOList = JsonUtils.toList(json.toString(), ActivityInfoResDTO.class);
        //3.将tabType对应activityStatus
        Integer selectStatus = ObjectUtils.equals(tabType, ActivityStatusEnum.NO_DISTRIBUTE.getStatus()) ?
                ActivityStatusEnum.DISTRIBUTING.getStatus() : ActivityStatusEnum.NO_DISTRIBUTE.getStatus();
        //4.遍历list集合，筛选出用户需要的数据
        List<ActivityInfoResDTO> resultList = activityInfoResDTOList.stream().filter(x -> ObjectUtils.equals(selectStatus, getRealStatus(x, x.getDistributeStartTime(), x.getDistributeEndTime())))//获取到的预热数据可能出现状态不一致现象，需要重新查出新的状态
                .peek(x -> x.setRemainNum(x.getStockNum()))//优惠券剩余数量等于优惠券库存数量
                .collect(Collectors.toList());
        return resultList;
    }


    /**
     * 获取到的预热数据可能出现状态不一致现象，需要重新查出新的状态
     *
     * @param dto
     * @param distributeStartTime
     * @param distributeEndTime
     * @return 真实状态
     */
    @Override
    public Integer getRealStatus(ActivityInfoResDTO dto, LocalDateTime distributeStartTime, LocalDateTime distributeEndTime) {
        //1.活动状态为未开始，到达了开始时间变为进行中
        if (ObjectUtils.equals(dto.getStatus(), ActivityStatusEnum.NO_DISTRIBUTE.getStatus())
                && LocalDateTime.now().isAfter(distributeStartTime) && LocalDateTime.now().isBefore(distributeEndTime)) {
            dto.setStatus(ActivityStatusEnum.DISTRIBUTING.getStatus());
            return ActivityStatusEnum.DISTRIBUTING.getStatus();
        }
        //2.活动状态为未开始，过了结束时间变为已过期
        if (ObjectUtils.equals(dto.getStatus(), ActivityStatusEnum.NO_DISTRIBUTE.getStatus()) && LocalDateTime.now().isAfter(distributeEndTime)) {
            dto.setStatus(ActivityStatusEnum.LOSE_EFFICACY.getStatus());
            return ActivityStatusEnum.LOSE_EFFICACY.getStatus();
        }
        //3.活动状态为进行中，过了结束时间变为已过期
        if (ObjectUtils.equals(dto.getStatus(), ActivityStatusEnum.DISTRIBUTING.getStatus()) && LocalDateTime.now().isAfter(distributeEndTime)) {
            dto.setStatus(ActivityStatusEnum.LOSE_EFFICACY.getStatus());
            return ActivityStatusEnum.LOSE_EFFICACY.getStatus();
        }
        return dto.getStatus();
    }


    /**
     * 存库数量-1 原子性操作
     */
    @Override
    public boolean decrStockNum(Long activityId) {
        //update activity set stock_num=stock_num-1 where id=? and stock_num>0
        boolean result = this.lambdaUpdate()
                .setSql(" stock_num=stock_num-1 ")
                .eq(Activity::getId, activityId)
                .gt(Activity::getStockNum, 0)
                .update();
        return result;
    }
}
